/*
	This file is part of Floculate.
	Copyright (C) 2008  Bill Whitacre

	Floculate is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	Floculate is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef RAT_MATH
#define RAT_MATH

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

#ifndef MATH_PI
#	ifndef M_PI
#		define MATH_PI 3.14159f
#	else
#		define MATH_PI M_PI
#	endif
#endif

/*
 * This was done while I had the flu for three weeks.  It therefore has
 * plenty of room for improvement.  It basically just does a bunch of matrix
 * and vector math commonly used in simulation applications.  Maybe I should
 * also add a RatMathVector2 for 2D stuff as well.  The RatMathMatrix4 needs no
 * 3x3 counterpart because that would techincally REDUCE efficiency since all
 * I had to do in this case was 3x3 matrix functions that operate on a 4x4
 * matrix.  It's all about speed, yeah?  Anyway, the code for the math really
 * acts as it's own documentation, but I've documented the header file
 * thoroughly as I feel like.
 */

/* Data structure for a 4x4/3x3 matrix. */
typedef struct RatMathMatrix4
{
	float mat00,mat10,mat20,mat30;
	float mat01,mat11,mat21,mat31;
	float mat02,mat12,mat22,mat32;
	float mat03,mat13,mat23,mat33;
} RatMathMatrix4;

/* Data structure for a 3D vector */
typedef struct RatMathVector3
{
	float x;
	float y;
	float z;
} RatMathVector3;

/* Data structure for a Quaternion, which is really a 4D vector */
typedef struct RatMathQuaternion
{
	float x;
	float y;
	float z;
	float w;
} RatMathQuaternion;

/* An euler is expressed in the same terms as a vector so need for another type. */
typedef RatMathVector3 RatMathEuler;

/* 3D camera frustum, 4D planar space. */
typedef struct RatMathFrustum
{
	float pleft,pright;
	float pbottom,ptop;
	float pnear,pfar;
} RatMathFrustum;

typedef struct RatMathArcBall
{
	RatMathVector3 grabvector;
    RatMathVector3 dragvector;
    float ex,wy,width,height;
} RatMathArcBall;

/* 4x4 Matrix Operations */

RatMathMatrix4 *RatMathMatrix4Create(); /* Return memory containing a fresh matrix. */
/* Return memory containing a transform matrix created from a quaternion. */
RatMathMatrix4 *RatMathMatrix4CreateFromQuaternion(RatMathQuaternion *quat);

/* Write a 4x4 identity matrix. */
void RatMathMatrix4WriteIdentity(RatMathMatrix4 *mat);
/* Write a 3x3 identity matrix. */
void RatMathMatrix4WriteIdentity3(RatMathMatrix4 *mat);
void RatMathMatrix4WriteZeroes(RatMathMatrix4 *mat); /* Clear amatrix. */
void RatMathMatrix4WriteOnes(RatMathMatrix4 *mat); /* Fill a matrix with ones */

/* Copy the contents of one matrix to the other. */
void RatMathMatrix4Copy(RatMathMatrix4 *mat0,RatMathMatrix4 *mat1);
/* Create memory containing a copy of a matrix */
RatMathMatrix4 *RatMathMatrix4Clone(RatMathMatrix4 *mat);

/* Extract the up vector of a transform matrix. */
void RatMathMatrix4ExtractUpVector(RatMathMatrix4 *mat,RatMathVector3 *up);
/* Extract the right vector of a transform matrix. */
void RatMathMatrix4ExtractRightVector(RatMathMatrix4 *mat,RatMathVector3 *rt);
/* Extract the look (pointing, foreward, etc.) vector of a transform matrix. */
void RatMathMatrix4ExtractLookVector(RatMathMatrix4 *mat,RatMathVector3 *look);
/* Extract the pos vector (translation) of a transform matrix. */
void RatMathMatrix4ExtractPosVector(RatMathMatrix4 *mat,RatMathVector3 *pos);

/* Write an up vector to a transform matrix. */
void RatMathMatrix4WriteUpVector(RatMathMatrix4 *mat,RatMathVector3 *up);
/* Write a right vector to a transform matrix. */
void RatMathMatrix4WriteRightVector(RatMathMatrix4 *mat,RatMathVector3 *rt);
/* Write a look (pointing, foreward, etc.) vector to a transform matrix. */
void RatMathMatrix4WriteLookVector(RatMathMatrix4 *mat,RatMathVector3 *look);
/* Write a pos vector (translation) to a transform matrix. */
void RatMathMatrix4WritePosVector(RatMathMatrix4 *mat,RatMathVector3 *pos);

void RatMathMatrix4Invert(RatMathMatrix4 *mat); /* Invert matrix */
void RatMathMatrix4Transpose(RatMathMatrix4 *mat); /* Transpose matrix */

/* Multiply a matrix by another matrix. */
void RatMathMatrix4Mult(RatMathMatrix4 *mat0,RatMathMatrix4 *mat1);

/* Apply a vector translation to a transform matrix. */
void RatMathMatrix4Translate(RatMathMatrix4 *mat,RatMathVector3 *vec);
/* Apply a scale vector to a transform matrix. */
void RatMathMatrix4Scale(RatMathMatrix4 *mat,RatMathVector3 *vec);
/* Apply an euler rotation to a transform matrix. */
void RatMathMatrix4Rotate(RatMathMatrix4 *mat,RatMathEuler *eul);

/* Express camera frustum data for a projection matrix. */
void RatMathMatrix4WriteFrustum(RatMathMatrix4 *mat,RatMathFrustum *frustum);
/* Multiply camera frustum data for a projection matrix. */
void RatMathMatrix4MultFrustum(RatMathMatrix4 *mat,RatMathFrustum *frustum);

/* Express the matrix as an array of floats. */
void RatMathMatrix4GetArray(RatMathMatrix4 *mat,float newmat[16]);
/* Express the transpose of the matrix as an array of floats. */
void RatMathMatrix4GetTransposeArray(RatMathMatrix4 *mat,float newmat[16]);


/* 3 Dim Vector (treated sometimes as eulers) Operations */

RatMathVector3 *RatMathVector3Create(); /* Return memory containting a fresh vector. */
/* Return memory containing a vector created from a quaternion. */
RatMathVector3 *RatMathVector3CreateFromQuaternion(RatMathQuaternion *quat);

void RatMathVector3WriteZeroes(RatMathVector3 *vec); /* Zero a vector. */

/* Copy the contents of one vector to the other. */
void RatMathVector3Copy(RatMathVector3 *vec0,RatMathVector3 *vec1);
/* Create memory containing a copy of a vector. */
RatMathVector3 *RatMathVector3Clone(RatMathVector3 *vec);

void RatMathVector3Add(RatMathVector3 *vec0,RatMathVector3 *vec1); /* Add two vectors. */
/* Subtract one vector from the other. */
void RatMathVector3Sub(RatMathVector3 *vec0,RatMathVector3 *vec1);
/* Multiply a vector by a scalar. */
void RatMathVector3Mult(RatMathVector3 *vec,float val);
/* Multiply a vector by a quaternion. */
void RatMathVector3MultQuaternion(RatMathVector3 *vec,RatMathQuaternion *quat);
/* Divide a vector by a value. */
void RatMathVector3Div(RatMathVector3 *vec,float val);

/* Return the dot product of two vectors. */
float RatMathVector3Dot(RatMathVector3 *vec0,RatMathVector3 *vec1);
/* Create memory containing the cross product of two vectors. */
RatMathVector3 *RatMathVector3Cross(RatMathVector3 *vec0,RatMathVector3 *vec1);

void RatMathVector3Normalize(RatMathVector3 *vec); /* Normalize a vector. */
/*
 * Normalize a vector then multiply it by a scalar,
 * effectively setting the magnitude.
 */
void RatMathVector3SetMagnitude(RatMathVector3 *vec,float mag);

/*
 * The first returns the magnitude of a vector, but the second returns
 * the square of the magnitude, eliminating the square root step.
 */
float RatMathVector3GetMagnitude(RatMathVector3 *vec);
float RatMathVector3GetMagSquared(RatMathVector3 *vec);

float RatMathVector3GetPitch(RatMathVector3 *vec); /* Get the pitch of a vector. */
float RatMathVector3GetYaw(RatMathVector3 *vec); /* Get the yaw of a vector. */

/*
 * Return memory containing euler angles describing the rotation
 * of a transform matrix.
 */
RatMathEuler *RatMathEulerCreateFromMatrix4(RatMathMatrix4 *mat);
/* Same, except we get it from a vector instead. */
RatMathEuler *RatMathEulerCreateFromVector3(RatMathVector3 *vec);
RatMathEuler *RatMathEulerCreateFromVector3AndRoll(RatMathVector3 *vec,float roll);
/* From a quaternion instead. */
RatMathEuler *RatMathEulerCreateFromQuaternion(RatMathQuaternion *quat);


/*
 * Quaternion (4D Hyperspace Vector ) Operations
 * i^2==j^2==k^2==ijk==-1
 */

RatMathQuaternion *RatMathQuaternionCreate(); /* Return memory containing a fresh quaternion. */
/* Return memory containing a quaternion copied from a vector and a float. */
RatMathQuaternion *RatMathQuaternionCreateFromVector3(RatMathVector3 *vec,float w);
RatMathQuaternion *RatMathQuaternionCreateFromMatrix4(RatMathMatrix4 *mat);
RatMathQuaternion *RatMathQuaternionCreateFromAxisAngle(RatMathVector3 *axis,float angle);
RatMathQuaternion *RatMathQuaternionCreateFromEuler(RatMathEuler *euler);

void RatMathQuaternionNormalize(RatMathQuaternion *quat);
void RatMathQuaternionWriteIdentity(RatMathQuaternion *quat);
void RatMathQuaternionMult(RatMathQuaternion *quat0,RatMathQuaternion *quat1);

void RatMathQuaternionCopy(RatMathQuaternion *quat0,RatMathQuaternion *quat1,int copy_w);
RatMathQuaternion *RatMathQuaternionGetConjugate(RatMathQuaternion *quat);
RatMathQuaternion *RatMathQuaternionClone(RatMathQuaternion *quat,int clone_w);

/* Perform the infamous Slerp quaternion interpolation function. */
RatMathQuaternion *RatMathQuaternionSlerp(RatMathQuaternion *quat0,RatMathQuaternion *quat1,float t);


/* ArcBall rotation utility */

RatMathArcBall *RatMathArcBallCreate(float ex,float wy,float width,float height);
void RatMathArcBallMouseDim(RatMathArcBall *arcball,float ex,float wy,float width,float height);

void RatMathArcBallMapSphere(RatMathArcBall *arcball,float x,float y,RatMathVector3 *surfvec);
void RatMathArcBallGrab(RatMathArcBall *arcball,float x,float y);
void RatMathArcBallDrag(RatMathArcBall *arcball,float x,float y,RatMathQuaternion *rotq);


/* Debugging Functions */

void RatMathDumpMatrix4(RatMathMatrix4 *mat); /* Dump the contents of a matrix. */
/* Dump the transform vectors expressed in a transform matrix. */
void RatMathDumpMatrix4AsVectors(RatMathMatrix4 *mat);
void RatMathDumpVector3(RatMathVector3 *vec); /* Dump the contents of a vector. */
void RatMathDumpEuler(RatMathEuler *eul); /* Dump the contents of an euler. */
void RatMathDumpQuaternion(RatMathQuaternion *quat); /* Dump the contents of a quaternion. */

#endif
